{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "<center>\n",
    "<img src=\"https://github.com/oussou-dev/mlcourse.ai/blob/jupyter_french/img/ods_stickers.jpg?raw=true\">\n",
    "<br>    \n",
    "   \n",
    "\n",
    "<div style=\"font-weight: 700; font-size: 25px;\"> [mlcourse.ai](https://mlcourse.ai) - Open Machine Learning Course</div>\n",
    "\n",
    "\n",
    "<br>\n",
    "Auteur: [Yury Kashnitsky](https://yorko.github.io). Traduit et édité par [Christina Butsko](https://www.linkedin.com/in/christinabutsko/), [Nerses Bagiyan](https://www.linkedin.com/in/nersesbagiyan/), [Yulia Klimushina](https://www.linkedin.com/in/yuliya-klimushina-7168a9139), [Yuanyuan Pao](https://www.linkedin.com/in/yuanyuanpao/) et [Ousmane Cissé](https://github.com/oussou-dev). Ce matériel est soumis aux termes et conditions de la licence [Creative Commons CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). L'utilisation gratuite est autorisée à des fins non commerciales.</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "# <center>Topic 4. Classification et régression linéaires\n",
    "## <center> Partie 3. Un exemple illustratif de régularisation de régression logistique</center></center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Dans le premier article, nous avons montré comment les caractéristiques polynomiales permettent aux modèles linéaires de créer des surfaces de séparation non linéaires. Montrons maintenant le visuellement.\n",
    "\n",
    "Voyons comment la régularisation affecte la qualité de la classification sur un jeu de données sur les tests de micropuce du cours d'Andrew Ng sur l'apprentissage automatique. Nous allons utiliser la régression logistique avec les caractéristiques polynomiales et faire varier le paramètre de régularisation $C$. Dans un premier temps, nous verrons comment la régularisation affecte la limite de séparation du classificateur et reconnaît intuitivement le sous-ajustement et le surajustement. Ensuite, nous choisirons le paramètre de régularisation pour qu'il soit numériquement proche de la valeur optimale via (`cross-validation`) et (` GridSearch`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# we don't like warnings\n",
    "# you can comment the following 2 lines if you'd like to\n",
    "import warnings\n",
    "\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import seaborn as sns\n",
    "from matplotlib import pyplot as plt\n",
    "from sklearn.linear_model import LogisticRegression, LogisticRegressionCV\n",
    "from sklearn.model_selection import GridSearchCV, StratifiedKFold, cross_val_score\n",
    "from sklearn.preprocessing import PolynomialFeatures"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Chargeons les données en utilisant `read_csv` de la bibliothèque` pandas`. Dans cet ensemble de données sur 118 micropuces (objets), il existe des résultats pour deux tests de contrôle de la qualité (deux variables numériques) et une information indiquant si la micropuce est entrée en production. Les variables sont déjà centrées, ce qui signifie que les valeurs de colonne ont été soustraites de leurs propres valeurs moyennes. Ainsi, la micropuce \"moyenne\" correspond à une valeur nulle dans les résultats du test."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# loading data\n",
    "data = pd.read_csv(\n",
    "    \"../../data/microchip_tests.txt\", header=None, names=(\"test1\", \"test2\", \"released\")\n",
    ")\n",
    "# getting some info about dataframe\n",
    "data.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Inspectons les 5 premières et dernières lignes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data.head(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data.tail(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Nous devons maintenant enregistrer le training set et les étiquettes de classe cible dans des tableaux NumPy distincts."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = data.iloc[:, :2].values\n",
    "y = data.iloc[:, 2].values"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "En tant qu'étape intermédiaire, nous pouvons tracer les données. Les points orange correspondent aux puces défectueuses, les bleues aux normales."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.scatter(X[y == 1, 0], X[y == 1, 1], c=\"blue\", label=\"Released\")\n",
    "plt.scatter(X[y == 0, 0], X[y == 0, 1], c=\"orange\", label=\"Faulty\")\n",
    "plt.xlabel(\"Test 1\")\n",
    "plt.ylabel(\"Test 2\")\n",
    "plt.title(\"2 tests of microchips. Logit with C=1\")\n",
    "plt.legend();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Définissons une fonction pour afficher la courbe de séparation du classifieur."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_boundary(clf, X, y, grid_step=0.01, poly_featurizer=None):\n",
    "    x_min, x_max = X[:, 0].min() - 0.1, X[:, 0].max() + 0.1\n",
    "    y_min, y_max = X[:, 1].min() - 0.1, X[:, 1].max() + 0.1\n",
    "    xx, yy = np.meshgrid(\n",
    "        np.arange(x_min, x_max, grid_step), np.arange(y_min, y_max, grid_step)\n",
    "    )\n",
    "\n",
    "    # to every point from [x_min, m_max]x[y_min, y_max]\n",
    "    # we put in correspondence its own color\n",
    "    Z = clf.predict(poly_featurizer.transform(np.c_[xx.ravel(), yy.ravel()]))\n",
    "    Z = Z.reshape(xx.shape)\n",
    "    plt.contour(xx, yy, Z, cmap=plt.cm.Paired)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Nous définissons les caractéristiques polynomiales suivantes du degré $d$ pour deux variables $x_1$ et $x_2$:\n",
    "\n",
    "$$\\large \\{x_1^d, x_1^{d-1}x_2, \\ldots x_2^d\\} =  \\{x_1^ix_2^j\\}_{i+j=d, i,j \\in \\mathbb{N}}$$\n",
    "\n",
    "Par exemple, pour $d=3$, ce seront les caractéristiques suivantes:\n",
    "\n",
    "$$\\large 1, x_1, x_2,  x_1^2, x_1x_2, x_2^2, x_1^3, x_1^2x_2, x_1x_2^2, x_2^3$$\n",
    "\n",
    "Dessiner un triangle de Pythagore montrerait combien de caractéristiques il y aura pour $d=4,5...$ et ainsi de suite.\n",
    "Le nombre de telles caractéristiques est exponentiellement grand et il peut être coûteux de créer des caractéristiques polynomiales de grand degré (par exemple, $d=10$) pour 100 variables. Plus important encore, ce n'est pas nécessaire."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Nous allons utiliser l'implémentation de `sklearn` de la régression logistique. Nous créons donc un objet qui ajoutera des caractéristiques polynomiales jusqu'au degré 7 dans la matrice $X$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "poly = PolynomialFeatures(degree=7)\n",
    "X_poly = poly.fit_transform(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_poly.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Entraînons la régression logistique avec le paramètre de régularisation $C = 10^{-2}$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "C = 1e-2\n",
    "logit = LogisticRegression(C=C, random_state=17)\n",
    "logit.fit(X_poly, y)\n",
    "\n",
    "plot_boundary(logit, X, y, grid_step=0.01, poly_featurizer=poly)\n",
    "\n",
    "plt.scatter(X[y == 1, 0], X[y == 1, 1], c=\"blue\", label=\"Released\")\n",
    "plt.scatter(X[y == 0, 0], X[y == 0, 1], c=\"orange\", label=\"Faulty\")\n",
    "plt.xlabel(\"Test 1\")\n",
    "plt.ylabel(\"Test 2\")\n",
    "plt.title(\"2 tests of microchips. Logit with C=%s\" % C)\n",
    "plt.legend()\n",
    "\n",
    "print(\"Accuracy on training set:\", round(logit.score(X_poly, y), 3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Nous pourrions maintenant essayer d’augmenter $C$ à 1. Ce faisant, nous affaiblissons la régularisation et la solution peut maintenant avoir de plus grandes valeurs (en valeur absolue) de poids de modèle que précédemment. Maintenant, la précision du classifieur sur le jeu d’entraînement s’améliore à 0.831."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "C = 1\n",
    "logit = LogisticRegression(C=C, random_state=17)\n",
    "logit.fit(X_poly, y)\n",
    "\n",
    "plot_boundary(logit, X, y, grid_step=0.005, poly_featurizer=poly)\n",
    "\n",
    "plt.scatter(X[y == 1, 0], X[y == 1, 1], c=\"blue\", label=\"Released\")\n",
    "plt.scatter(X[y == 0, 0], X[y == 0, 1], c=\"orange\", label=\"Faulty\")\n",
    "plt.xlabel(\"Test 1\")\n",
    "plt.ylabel(\"Test 2\")\n",
    "plt.title(\"2 tests of microchips. Logit with C=%s\" % C)\n",
    "plt.legend()\n",
    "\n",
    "print(\"Accuracy on training set:\", round(logit.score(X_poly, y), 3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Alors, pourquoi n'augmentons-nous pas encore plus $C$ - jusqu'à 10 000? Maintenant, la régularisation n’est clairement pas assez forte et nous constatons des sur-ajustements. Notez que, avec $C$ = 1 et une limite \"lisse\", le nombre de réponses correctes sur l'ensemble d'apprentissage n'est pas beaucoup plus faible qu'ici. Mais on peut facilement imaginer comment notre deuxième modèle fonctionnera beaucoup mieux avec les nouvelles données."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "C = 1e4\n",
    "logit = LogisticRegression(C=C, random_state=17)\n",
    "logit.fit(X_poly, y)\n",
    "\n",
    "plot_boundary(logit, X, y, grid_step=0.005, poly_featurizer=poly)\n",
    "\n",
    "plt.scatter(X[y == 1, 0], X[y == 1, 1], c=\"blue\", label=\"Released\")\n",
    "plt.scatter(X[y == 0, 0], X[y == 0, 1], c=\"orange\", label=\"Faulty\")\n",
    "plt.xlabel(\"Test 1\")\n",
    "plt.ylabel(\"Test 2\")\n",
    "plt.title(\"2 tests of microchips. Logit with C=%s\" % C)\n",
    "plt.legend()\n",
    "\n",
    "print(\"Accuracy on training set:\", round(logit.score(X_poly, y), 3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Pour discuter des résultats, réécrivons la fonction optimisée dans la régression logistique avec le formulaire:\n",
    "\n",
    "$$\\large J(X,y,w) = \\mathcal{L} + \\frac{1}{C}||w||^2,$$\n",
    "\n",
    "où\n",
    "\n",
    "- $\\mathcal{L}$ est la fonction de perte logistique résumée sur le jeu de données\n",
    "- $C$ est le coefficient de régularisation inverse (le même $C$ de l'implémentation de `sklearn` de` LogisticRegression`)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "**Sous-totaux**:\n",
    "- plus le paramètre $C$ est grand, plus les relations dans les données que le modèle peut récupérer sont complexes ($C$ correspond intuitivement à la \"complexité\" du modèle - capacité du modèle)\n",
    "- si la régularisation est trop forte, c’est-à-dire que les valeurs de $C$ sont faibles, la solution au problème de la minimisation de la fonction de perte logistique peut être celle où beaucoup des poids sont trop petits ou mis à zéro. Le modèle n’est pas non plus suffisamment \"pénalisé\" pour les erreurs (c’est-à-dire que dans la fonction $J$, la somme des carrés des poids \"l'emporte sur\" et l'erreur $\\mathcal{L}$ peut être relativement grande). Dans ce cas, le modèle sera insuffisant, comme nous l’avons vu dans notre premier cas.\n",
    "- au contraire, si la régularisation est trop faible, c'est-à-dire que les valeurs de $C$ sont grandes, un vecteur $w$ avec des composantes de valeur absolue élevées peut devenir la solution au problème d'optimisation. Dans ce cas, $\\mathcal{L}$ a une plus grande contribution à la $J$ fonctionnelle optimisée. En gros, le modèle a trop \"peur\" de se tromper sur les objets de l’entraînement et va donc sur-adapter comme nous l’avons vu dans le troisième cas.\n",
    "- la régression logistique ne \"comprendra\" pas (ni n' \"apprendra\") quelle valeur choisir pour $C$ comme pour les poids $w$. C'est-à-dire qu'il ne peut pas être déterminé en résolvant le problème d'optimisation dans la régression logistique. Nous avons déjà vu une situation similaire auparavant - un arbre de décision ne peut pas \"apprendre\" quelle limite de profondeur choisir pendant le processus de formation. Par conséquent, $C$ est un hyperparamètre de modèle adapté à la validation croisée; il en est de même de max_depth dans un arbre."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "**Réglage des paramètres de régularisation**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "En utilisant cet exemple, identifions la valeur optimale du paramètre de régularisation $C$. Cela peut être fait en utilisant `LogisticRegressionCV` - une recherche sur grille de paramètres suivie d'une validation croisée. Cette classe est conçue spécifiquement pour la régression logistique (algorithmes efficaces avec des paramètres de recherche bien connus). Pour un modèle arbitraire, utilisez `GridSearchCV`,` RandomizedSearchCV` ou des algorithmes spéciaux pour l'optimisation de l'hyperparamètre tels que celui implémenté dans `hyperopt`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=17)\n",
    "\n",
    "c_values = np.logspace(-2, 3, 500)\n",
    "\n",
    "logit_searcher = LogisticRegressionCV(Cs=c_values, cv=skf, verbose=1, n_jobs=-1)\n",
    "logit_searcher.fit(X_poly, y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "logit_searcher.C_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Pour voir comment la qualité du modèle (pourcentage de réponses correctes sur les ensembles d'apprentissage et de validation) varie avec l'hyperparamètre $C$, nous pouvons tracer le graphique."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(c_values, np.mean(logit_searcher.scores_[1], axis=0))\n",
    "plt.xlabel(\"C\")\n",
    "plt.ylabel(\"Mean CV-accuracy\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Enfin, sélectionner la zone avec les \"meilleures\" valeurs de $C$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(c_values, np.mean(logit_searcher.scores_[1], axis=0))\n",
    "plt.xlabel(\"C\")\n",
    "plt.ylabel(\"Mean CV-accuracy\")\n",
    "plt.xlim((0, 10));"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "Rappelez-vous que ces courbes s'appellent des courbes de validation. Avant, nous les construisions manuellement, mais sklearn dispose de méthodes spéciales pour les construire que nous utiliserons à l'avenir."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "lang": "fr"
   },
   "source": [
    "### Ressources utiles\n",
    "- Main course [site](https://mlcourse.ai), [course repo](https://github.com/Yorko/mlcourse.ai), and YouTube [channel](https://www.youtube.com/watch?v=QKTuw4PNOsU&amp;list=PLVlY_7IJCMJeRfZ68eVfEcu-UcN9BbwiX)\n",
    "- Medium [\"story\"](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-4-linear-classification-and-regression-44a41b9b5220) based on this notebook\n",
    "- Course materials as a [Kaggle Dataset](https://www.kaggle.com/kashnitsky/mlcourse)\n",
    "- If you read Russian: an [article](https://habrahabr.ru/company/ods/blog/323890/) on Habrahabr with ~ the same material. And a [lecture](https://youtu.be/oTXGQ-_oqvI) on YouTube\n",
    "- A nice and concise overview of linear models is given in the book [“Deep Learning”](http://www.deeplearningbook.org) (I. Goodfellow, Y. Bengio, and A. Courville).\n",
    "- Linear models are covered practically in every ML book. We recommend “Pattern Recognition and Machine Learning” (C. Bishop) and “Machine Learning: A Probabilistic Perspective” (K. Murphy).\n",
    "- If you prefer a thorough overview of linear model from a statistician’s viewpoint, then look at “The elements of statistical learning” (T. Hastie, R. Tibshirani, and J. Friedman).\n",
    "- The book “Machine Learning in Action” (P. Harrington) will walk you through implementations of classic ML algorithms in pure Python.\n",
    "- [Scikit-learn](http://scikit-learn.org/stable/documentation.html) library. These guys work hard on writing really clear documentation.\n",
    "- Scipy 2017 [scikit-learn tutorial](https://github.com/amueller/scipy-2017-sklearn) by Alex Gramfort and Andreas Mueller.\n",
    "- One more [ML course](https://github.com/diefimov/MTH594_MachineLearning) with very good materials.\n",
    "- [Implementations](https://github.com/rushter/MLAlgorithms) of many ML algorithms. Search for linear regression and logistic regression."
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "hide_input": false,
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.4"
  },
  "nbTranslate": {
   "displayLangs": [
    "*"
   ],
   "hotkey": "alt-t",
   "langInMainMenu": true,
   "sourceLang": "en",
   "targetLang": "fr",
   "useGoogleTranslate": true
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
